package org.zkzd.zkzdtools.StringReplace;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class DocxXmlReplacer {

    public static void main(String[] args) throws Exception {
        Map<String, String> replacements = new HashMap<>();
        replacements.put("{{huiqi}}", "2025年3月24日");
        replacements.put("danweimingc", "中科知道（北京）科技有限公司");

        processDocx("input.docx", "output.docx", replacements);
    }

    public static void processDocx(String inputPath, String outputPath,
                                   Map<String, String> replacements) throws Exception {
        // 检查并删除已存在的输出文件
        Path outputFilePath = Paths.get(outputPath);
        if (Files.exists(outputFilePath)) {
            try {
                Files.delete(outputFilePath);
            } catch (NoSuchFileException e) {
                // 文件不存在无需处理
            } catch (AccessDeniedException e) {
                throw new IOException("文件被占用或无删除权限: " + outputPath, e);
            }
        }

        Path tempDir = Files.createTempDirectory("docx_");
        try {
            unzip(inputPath, tempDir.toString());

            try (Stream<Path> pathStream = Files.walk(tempDir)) {
                pathStream.filter(path -> isTargetXml(path.toString()))
                        .forEach(path -> processXmlFile(path, replacements));
            }

            zip(tempDir.toString(), outputPath);
        } finally {
            deleteDirectory(tempDir.toFile());
        }
    }

    private static boolean isTargetXml(String path) {
        return path.matches(".*(document|header\\d+|footer\\d+)\\.xml$");
    }

    private static void processXmlFile(Path xmlPath, Map<String, String> replacements) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            Document doc = factory.newDocumentBuilder().parse(xmlPath.toFile());

            processNormalText(doc, replacements);
            processTextBoxes(doc, replacements);

            // 保存修改后的XML
            saveDocument(doc, xmlPath);
        } catch (Exception e) {
            throw new RuntimeException("Error processing: " + xmlPath, e);
        }
    }

    private static void processNormalText(Document doc, Map<String, String> replacements) {
        NodeList textNodes = doc.getElementsByTagNameNS(
                "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
                "t");

        processNodes(textNodes, replacements);
    }


    private static void processTextBoxes(Document doc, Map<String, String> replacements) {
        try {
            XPath xpath = XPathFactory.newInstance().newXPath();

            // 设置自定义命名空间上下文
            Map<String, String> namespaces = new HashMap<>();
            namespaces.put("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
            namespaces.put("wps", "http://schemas.microsoft.com/office/word/2010/wordprocessingShape");
            xpath.setNamespaceContext(new CustomNamespaceContext(namespaces));

            // XPath表达式保持不变
            NodeList txbxNodes = (NodeList) xpath.evaluate(
                    "//wps:txbx/w:txbxContent//w:t",
                    doc,
                    XPathConstants.NODESET
            );

            processNodes(txbxNodes, replacements);
        } catch (Exception e) {
            throw new RuntimeException("XPath error", e);
        }
    }

    private static void processNodes(NodeList nodes, Map<String, String> replacements) {
        for (int i = 0; i < nodes.getLength(); i++) {
            Node textNode = nodes.item(i);
            String original = textNode.getTextContent();
            String replaced = original;

            for (Map.Entry<String, String> entry : replacements.entrySet()) {
                replaced = replaced.replace(entry.getKey(), entry.getValue());
            }

            if (!replaced.equals(original)) {
                textNode.setTextContent(replaced);
            }
        }
    }

    private static void saveDocument(Document doc, Path path) throws Exception {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        try (OutputStream os = Files.newOutputStream(path)) {
            transformer.transform(new DOMSource(doc), new StreamResult(os));
        }
    }

    private static void unzip(String zipPath, String outputDir) throws IOException {
        try (ZipFile zipFile = new ZipFile(zipPath)) {
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                Path outputPath = Paths.get(outputDir, entry.getName());

                if (entry.isDirectory()) {
                    Files.createDirectories(outputPath);
                } else {
                    // 确保父目录存在
                    if (outputPath.getParent() != null) {
                        Files.createDirectories(outputPath.getParent());
                    }

                    // 检查是否为零长度文件
                    if (entry.getSize() == 0) {
                        Files.createFile(outputPath);
                    } else {
                        try (InputStream is = zipFile.getInputStream(entry)) {
                            Files.copy(is, outputPath, StandardCopyOption.REPLACE_EXISTING);
                        }
                    }
                }
            }
        } catch (FileAlreadyExistsException e) {
            // 忽略已存在目录的异常
            if (!Files.isDirectory(Path.of(e.getFile()))) {
                throw e;
            }
        }
    }

    private static void zip(String sourceDir, String zipPath) throws IOException {
        Path sourcePath = Paths.get(sourceDir);
        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath));
             Stream<Path> pathStream = Files.walk(sourcePath)) { // 添加try-with-resources

            pathStream.filter(path -> !Files.isDirectory(path))
                    .forEach(path -> {
                        String entryName = sourcePath.relativize(path).toString().replace('\\', '/');
                        try {
                            zos.putNextEntry(new ZipEntry(entryName));
                            Files.copy(path, zos);
                            zos.closeEntry();
                        } catch (IOException e) {
                            throw new UncheckedIOException("文件打包失败: " + path, e);
                        }
                    });
        } catch (UncheckedIOException e) {
            throw e.getCause(); // 解包原始异常
        }
    }

    private static void deleteDirectory(File dir) throws IOException {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                deleteDirectory(file);
            }
        }

        // 添加删除结果检查
        if (!dir.delete()) {
            Path path = dir.toPath();
            if (Files.exists(path)) {
                throw new IOException("无法删除文件/目录: " + dir.getAbsolutePath());
            }
        }
    }

    static class CustomNamespaceContext implements NamespaceContext {
        private final Map<String, String> prefixToUri;
        private final Map<String, String> uriToPrefix;

        public CustomNamespaceContext(Map<String, String> prefixToUri) {
            this.prefixToUri = new HashMap<>(prefixToUri);
            this.uriToPrefix = new HashMap<>();
            for (Map.Entry<String, String> entry : prefixToUri.entrySet()) {
                uriToPrefix.put(entry.getValue(), entry.getKey());
            }
        }

        public String getNamespaceURI(String prefix) {
            return prefixToUri.getOrDefault(prefix, null);
        }

        public String getPrefix(String namespaceURI) {
            return uriToPrefix.getOrDefault(namespaceURI, null);
        }

        public Iterator<String> getPrefixes(String namespaceURI) {
            String prefix = getPrefix(namespaceURI);
            if (prefix != null) {
                return Collections.singleton(prefix).iterator();
            }
            return Collections.emptyIterator();
        }
    }
}